package xin.nick.cloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.domain.Example;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import xin.nick.cloud.dao.TokenDao;
import xin.nick.cloud.pojo.Token;

import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author Nick
 * @date 2021/8/1
 * @description 授权验证
 */
@Slf4j
@Component
@RefreshScope
public class AuthFilter implements GlobalFilter, Ordered {

    private static final String TOKEN = "token";
    private static final String URL_SPLIT_FLAG = ";";

    @Autowired
    private TokenDao tokenDao;

    @Value("${my.exclude.path}")
    private String excludePath;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 从上下⽂中取出request
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 排除的列表不需要拦截
        URI uri = request.getURI();
        String path = uri.getPath();
        for (String excludePath : excludePath.split(URL_SPLIT_FLAG)) {
            if (path.indexOf(excludePath) != -1) {
                return chain.filter(exchange);
            }
        }

        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        // 没有值的返回
        if (Objects.isNull(cookies) || CollectionUtils.isEmpty(cookies.get(TOKEN))) {
            response.setStatusCode(HttpStatus.OK);
            log.info("鉴权失败");
            String data = "fail";
            DataBuffer wrap =
                    response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }

        // 不合法的返回
        List<HttpCookie> tokenList = cookies.get(TOKEN);
        HttpCookie httpCookie = tokenList.get(0);
        String tokenValue = httpCookie.getValue();
        if (Objects.isNull(tokenValue)) {
            response.setStatusCode(HttpStatus.OK);
            log.info("鉴权失败");
            String data = "fail";
            DataBuffer wrap =
                    response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }

        // 没查到也返回
        Token tokenQuery = new Token();
        tokenQuery.setToken(tokenValue);
        Example example = Example.of(tokenQuery);
        Optional one = tokenDao.findOne(example);
        if (one.isEmpty() || Objects.isNull(one.get())) {

            response.setStatusCode(HttpStatus.OK);
            log.info("鉴权失败");
            String data = "fail";
            DataBuffer wrap =
                    response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }

        // 放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 1;
    }

    private Mono<Void> goLoginPage(ServerHttpResponse response) {
        String redirectUrl = "http://local.nick.xin/static/login.html";
        log.info("鉴权失败重定向到URL: {}", redirectUrl);
        response.getHeaders().set(HttpHeaders.LOCATION, redirectUrl);
        //303状态码表示由于请求对应的资源存在着另一个URI，应使用GET方法定向获取请求的资源
        response.setStatusCode(HttpStatus.SEE_OTHER);
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        return response.setComplete();
    }
}
